/*
 * Copyright (C) 2008 Alessandro Sivieri <alessandro.sivieri@chimera-bellerofonte.eu>.
 *
 *  This file is part of Daex.
 *
 *  Daex is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Foobar is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.chimera_bellerofonte.tesi.datatypes;

import java.io.File;
import java.io.Serializable;

import eu.chimera_bellerofonte.tesi.encoding.Encoder;



/**
 * Questa classe definisce tutte le informazioni che si e' scelto di memorizzare per le tracce dell'album richiesto, con i dettagli compilati dall'utente (alcuni dei quali eventualmente desunti dall'oggetto Album) ed alcune specifiche necessarie alla codifica. Implementa le interfacce Serializable e Cloneable per poter essere trasmessa via stream, e Comparable come utility generica.
 * @see java.lang.Cloneable
 * @see java.lang.Comparable
 * @see java.io.Serializable
 * @see eu.chimera_bellerofonte.tesi.datatypes.Album
 * @author  Sivieri Alessandro
 * @author  Vitucci Nicola
 * @version  20/apr/07
 */
public class Song implements Serializable, Cloneable, Comparable<Song>
{
	static final long serialVersionUID = 1L;
	private String artist=null;
	private String title=null;
	private String album=null;
	private Genre genre=null;
	private int startChapter=0;
	private int endChapter=0;
	private int tableValue=0;
	private int absoluteValue=0;
	private Encoder encoder=null;
	private File tempFile=null;
	
	/**
	 * Costruttore vuoto e con visibilita' di package per evitare
	 * che venga utilizzato a sproposito; creato per comodita'
	 * nel caricamento di dati salvati.
	 */
	Song()
	{
		
	}
	
	/**
	 * Costruttore minimo: le informazioni non quivi specificate
	 * sono desunte dall'album che contiene questa traccia.
	 * 
	 * @param title il titolo della canzone
	 * @param startChapter il capitolo di inizio
	 * @param endChapter il capitolo di fine (puo' coincidere col precedente)
	 */
	public Song(String title, int startChapter, int endChapter)
	{
		this.title = title;
		this.startChapter = startChapter;
		this.endChapter = endChapter;
		this.tableValue=startChapter;
		this.absoluteValue=startChapter;
		this.artist=Album.GENERICARTIST;
		this.album=Album.GENERICALBUM;
		this.genre=Album.GENERICGENRE;
		this.title=Album.GENERICTITLE;
	}
	
	/**
	 * Costruttore completo per la specifica completa delle informazioni
	 * da memorizzare.
	 * 
	 * @param artist artista della canzone
	 * @param title il numero della canzone indicata
	 * @param album album di appartenenza
	 * @param genre genere musicale
	 * @param startChapter capitolo d'inizio
	 * @param endChapter capitolo di fine (puo' coincidere col precedente)
	 */
	public Song(String artist, int title, String album, Genre genre, int startChapter, int endChapter)
	{
		this.artist = artist;
		this.title = Album.GENERICTITLE+title;
		this.album = album;
		this.genre = genre;
		this.startChapter = startChapter;
		this.endChapter = endChapter;
		this.tableValue=startChapter;
		this.absoluteValue=title;
	}

	/**
	 * Costruttore completo per la specifica completa delle informazioni
	 * da memorizzare.
	 * 
	 * @param artist artista della canzone
	 * @param title titolo della canzone
	 * @param album album di appartenenza
	 * @param genre genere musicale
	 * @param startChapter capitolo d'inizio
	 * @param endChapter capitolo di fine (puo' coincidere col precedente)
	 */
	public Song(String artist, String title, String album, Genre genre, int startChapter, int endChapter)
	{
		this.artist = artist;
		this.title = title;
		this.album = album;
		this.genre = genre;
		this.startChapter = startChapter;
		this.endChapter = endChapter;
		this.tableValue=startChapter;
	}

	/**
	 * Questo metodo ritorna il valore attuale di album.
	 * @return  il valore attuale di album
	 * @uml.property  name="album"
	 */
	public String getAlbum()
	{
		return album;
	}

	/**
	 * Questo metodo setta album con il valore dato.
	 * @param album  il nuovo valore per questo campo
	 * @uml.property  name="album"
	 */
	public void setAlbum(String album)
	{
		this.album = album;
	}

	/**
	 * Questo metodo ritorna il valore attuale di artist.
	 * @return  il valore attuale di artist
	 * @uml.property  name="artist"
	 */
	public String getArtist()
	{
		return artist;
	}

	/**
	 * Questo metodo setta artist con il valore dato.
	 * @param artist  il nuovo valore per questo campo
	 * @uml.property  name="artist"
	 */
	public void setArtist(String artist)
	{
		this.artist = artist;
	}

	/**
	 * Questo metodo ritorna il valore attuale di endChapter.
	 * @return  il valore attuale di endChapter
	 * @uml.property  name="endChapter"
	 */
	public int getEndChapter()
	{
		return endChapter;
	}

	/**
	 * Questo metodo setta endChapter con il valore dato.
	 * @param endChapter  il nuovo valore per questo campo
	 * @uml.property  name="endChapter"
	 */
	public void setEndChapter(int endChapter)
	{
		this.endChapter = endChapter;
	}

	/**
	 * Questo metodo ritorna il valore attuale di genre.
	 * @return  il valore attuale di genre
	 * @uml.property  name="genre"
	 */
	public Genre getGenre()
	{
		return genre;
	}

	/**
	 * Questo metodo setta genre con il valore dato.
	 * @param genre  il nuovo valore per questo campo
	 * @uml.property  name="genre"
	 */
	public void setGenre(Genre genre)
	{
		this.genre = genre;
	}

	/**
	 * Questo metodo ritorna il valore attuale di startChapter.
	 * @return  il valore attuale di startChapter
	 * @uml.property  name="startChapter"
	 */
	public int getStartChapter()
	{
		return startChapter;
	}

	/**
	 * Questo metodo setta startChapter con il valore dato.
	 * @param startChapter  il nuovo valore per questo campo
	 * @uml.property  name="startChapter"
	 */
	public void setStartChapter(int startChapter)
	{
		this.startChapter = startChapter;
	}

	/**
	 * Questo metodo ritorna il valore attuale di title.
	 * @return  il valore attuale di title
	 * @uml.property  name="title"
	 */
	public String getTitle()
	{
		return title;
	}

	/**
	 * Questo metodo setta title con il valore dato.
	 * @param title  il nuovo valore per questo campo
	 * @uml.property  name="title"
	 */
	public void setTitle(String title)
	{
		this.title = title;
	}

	/**
	 * Questo metodo ritorna il valore attuale di tableValue.
	 * 
	 * @return il valore attuale di tableValue
	 */
	public int getTableValue()
	{
		return tableValue;
	}

	/**
	 * Questo metodo setta tableValue con il valore dato.
	 * 
	 * @param tableValue il nuovo valore per questo campo
	 */
	public void setTableValue(int tableValue)
	{
		this.tableValue = tableValue;
	}

	/**
	 * Questo metodo ritorna il valore attuale di encoder.
	 * 
	 * @return il valore attuale di encoder
	 */
	public Encoder getEncoder()
	{
		return encoder;
	}

	/**
	 * Questo metodo setta encoder con il valore dato.
	 * 
	 * @param encoder il nuovo valore per questo campo
	 */
	public void setEncoder(Encoder encoder)
	{
		this.encoder = encoder;
	}

	/**
	 * Questo metodo ritorna il valore attuale di tempFile.
	 * 
	 * @return il valore attuale di temp
	 */
	public File getTempFile()
	{
		return tempFile;
	}

	/**
	 * Questo metodo setta tempFile con il valore dato.
	 * 
	 * @param temp il nuovo valore per questo campo
	 */
	public void setTempFile(File temp)
	{
		this.tempFile = temp;
	}

	/**
	 * Questo metodo ritorna il valore attuale di absoluteValue.
	 * 
	 * @return il valore attuale di absoluteValue
	 */
	public int getAbsoluteValue()
	{
		return absoluteValue;
	}

	/*
	 * Questo metodo setta absoluteValue con il valore dato.
	 * Visibilita' limitata per rendere la proprieta' quasi
	 * read-only (escludendo il caso di caricamento da file).
	 * 
	 * @param absoluteValue il nuovo valore per questo campo
	 */
	void setAbsoluteValue(int absoluteValue)
	{
		this.absoluteValue = absoluteValue;
	}

	/**
	 * Confronto tra due oggetti: si confrontano i capitoli d'inizio
	 * e fine (posto che i titoli siano uguali) per stabilire un ordine.
	 * Se esistono due tracce con titoli differenti e stesso capitolo
	 * d'inizio viene lanciata una diversa eccezione.
	 * 
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 * @see eu.chimera_bellerofonte.tesi.datatypes.SongTitleException
	 * @param o un oggetto da confrontare con this
	 * @throws ClassCastException se l'oggetto specificato non e' un Song o per problemi sul titolo
	 * @throws SongTitleException se gli oggetti hanno titoli differenti ma lo stesso capitolo d'inizio
	 */
	public int compareTo(Song o)
	{
		if(getStartChapter()<(o.getStartChapter()))
		{
			return -1;
		}
		else if(getStartChapter()==(o.getStartChapter()))
		{
			if(getTitle().equals(o.getTitle()))
			{
				return 0;
			}
			else
			{
				throw new SongTitleException();
			}
		}
		else
		{
			return 1;
		}
	}

	/**
	 * Metodo per la creazione di un oggetto identico a questo;
	 * viene implementato richiamando costruttori differenti a seconda
	 * del tipo di oggetto memorizzato.
	 * 
	 * @see java.lang.Object#clone()
	 * @return un oggetto di tipo Song identico al precedente
	 * @throws CloneNotSupportedException se non e' supportata la deep copy
	 */
	@Override
	public Object clone() throws CloneNotSupportedException
	{
		Song s=null;
		if(album==null)
		{
			s=new Song(getTitle(), getStartChapter(), getEndChapter());
		}
		else
		{
			s=new Song(getArtist(), getTitle(), getAlbum(), getGenre(), getStartChapter(), getEndChapter());
		}
		return s;
	}

	/**
	 * Metodo per il confronto di tracce; confronta solo gli
	 * attributi di classe sicuramente inizializzati.
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 * @param obj un oggetto generico
	 */
	@Override
	public boolean equals(Object obj)
	{
		if(this==obj)
		{
			return true;
		}
		if(obj instanceof Song)
		{
			if(getTitle().equals((((Song) obj)).getTitle())
			   && getStartChapter()==((Song) obj).getStartChapter()
			   && getEndChapter()==((Song) obj).getEndChapter()
			   && getTableValue()==((Song) obj).getTableValue())
			{
				return true;
			}
		}
		return false;
	}

	/**
	 * Metodo per la rappresentazione del sistema; espone solo gli
	 * attributi di classe sicuramente inizializzati.
	 * 
	 * @see java.lang.Object#toString()
	 * @return una stringa rappresentante la traccia
	 */
	@Override
	public String toString()
	{
		return "Titolo: "+getTitle()+"\nCapitolo d'inizio: "+getStartChapter()+"\nCapitolo di fine: "+getEndChapter();
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode()
	{
		// non c'e' interesse a variarne l'implementazione,
		// si crea l'override solo come buona regola di implementazione
		return super.hashCode();
	}

}
